package web

import (
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"google.golang.org/protobuf/types/known/timestamppb"
	"net/http"
	"time"
	codev1 "webook/api/proto/gen/code/v1"
	userv1 "webook/api/proto/gen/user/v1"
	jwt3 "webook/bff/web/jwt"
	"webook/pkg/ginx"
	"webook/user/errs"
)

const (
	emailRegexPattern = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"
	// 和上面比起来，用 ` 看起来就比较清爽
	passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`
	phoneRegexPattern    = "^1[3-9]\\d{9}$"

	bizLogin = "login"
)

type UserHandler struct {
	svc              userv1.UserServiceClient
	codeSvc          codev1.CodeServiceClient
	emailRegexExp    *regexp.Regexp
	passwordRegexExp *regexp.Regexp
	phoneRegexExp    *regexp.Regexp
	jwt3.Handler
}

func NewUserHandler(svc userv1.UserServiceClient, codeSvc codev1.CodeServiceClient,
	jwthdl jwt3.Handler) *UserHandler {
	return &UserHandler{
		svc:              svc,
		codeSvc:          codeSvc,
		emailRegexExp:    regexp.MustCompile(emailRegexPattern, regexp.None),
		passwordRegexExp: regexp.MustCompile(passwordRegexPattern, regexp.None),
		phoneRegexExp:    regexp.MustCompile(phoneRegexPattern, regexp.None),
		Handler:          jwthdl,
	}
}

func (uh *UserHandler) RegisterRoutes(server *gin.Engine) {
	ug := server.Group("/users")
	ug.POST("/signup", ginx.WrapReq[SignUpReq](uh.SignUp))
	ug.POST("/login", ginx.WrapReq[LoginReq](uh.LoginJwt))

	ug.POST("/logout", uh.Logout)
	ug.POST("/edit", uh.Edit)
	ug.GET("/profile", uh.ProfileJwt)
	ug.POST("/login_sms/code/send", uh.SendSMSLoginCode)
	ug.POST("/login_sms", uh.LoginSMS)
	ug.POST("/refresh_token", uh.RefreshToken)
}

type SignUpReq struct {
	Email           string `json:"email"`
	Password        string `json:"password"`
	ConfirmPassword string `json:"confirmPassword"`
}

// SignUp 用户注册接口
func (uh *UserHandler) SignUp(ctx *gin.Context, req SignUpReq) (Result, error) {
	isEmail, err := uh.emailRegexExp.MatchString(req.Email)
	if err != nil {
		return Result{
			Code: errs.UserInternalServerError,
			Msg:  "系统错误",
		}, err
	}

	if !isEmail {
		return Result{
			Code: errs.UserInvalidInput,
			Msg:  "邮箱格式错误",
		}, nil
	}

	if req.Password != req.ConfirmPassword {
		return Result{
			Code: errs.UserInvalidInput,
			Msg:  "两次输入的密码不一致",
		}, nil
	}

	isPassword, err := uh.passwordRegexExp.MatchString(req.Password)
	if err != nil {
		return Result{
			Code: errs.UserInvalidInput,
			Msg:  "系统错误",
		}, err
	}

	if !isPassword {
		return Result{
			Code: errs.UserInvalidInput,
			Msg:  "密码必须包含数字、特殊字符，并且长度不能小于 8 位",
		}, nil
	}

	_, err = uh.svc.Signup(ctx.Request.Context(), &userv1.SignupRequest{
		User: &userv1.User{
			Email:    req.Email,
			Password: req.Password,
		},
	})

	if err != nil {
		return Result{
			Code: errs.UserInternalServerError,
			Msg:  "系统错误",
		}, err
	}
	return Result{
		Msg: "OK",
	}, nil
}

type LoginReq struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

func (uh *UserHandler) LoginJwt(ctx *gin.Context, req LoginReq) (Result, error) {
	u, err := uh.svc.Login(ctx.Request.Context(), &userv1.LoginRequest{
		Email:    req.Email,
		Password: req.Password,
	})

	if err != nil {
		return Result{
			Code: errs.UserInternalServerError,
			Msg:  "系统错误",
		}, err
	}
	err = uh.SetLoginToken(ctx, u.User.Id)
	if err != nil {
		return Result{}, err
	}
	return Result{Msg: "登陆成功"}, nil
}

// LoginSMS 手机号码登录
func (uh *UserHandler) LoginSMS(ctx *gin.Context) {
	type Req struct {
		Phone string `json:"phone"`
		Code  string `json:"code"`
	}

	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}

	resp, err := uh.codeSvc.Verify(ctx, &codev1.VerifyRequest{
		Biz:       bizLogin,
		Phone:     req.Phone,
		InputCode: req.Code,
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统异常"})
		return
	}

	if resp.Answer {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "验证码错误"})
		return
	}

	// 验证码是对的
	// 登录或者注册用户
	u, err := uh.svc.FindOrCreate(ctx, &userv1.FindOrCreateRequest{
		Phone: req.Phone,
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "系统错误"})
		return
	}
	// 用 uuid 来标识这一次会话
	// ssid := uuid.New().String()
	// err = uh.SetJwtToken(ctx, ssid, u.User.Id)
	err = uh.SetLoginToken(ctx, u.User.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Msg: "系统错误"})
		return
	}
	ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "登录成功"})
}

// SendSMSLoginCode 发送短信验证码
func (uh *UserHandler) SendSMSLoginCode(ctx *gin.Context) {
	type Req struct {
		Phone string `json:"phone"`
	}

	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}

	isPhone, err := uh.phoneRegexExp.MatchString(req.Phone)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: errs.UserInternalServerError, Msg: "系统错误"})
		return
	}

	if !isPhone {
		ctx.JSON(http.StatusOK, Result{Code: errs.UserInvalidInput, Msg: "手机号码格式错误"})
		return
	}

	_, err = uh.codeSvc.Send(ctx, &codev1.CodeSendRequest{
		Biz:   bizLogin,
		Phone: req.Phone,
	})

	switch err {
	case nil:
		ctx.JSON(http.StatusOK, Result{Msg: "验证码发送成功"})
	default:
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统错误"})
		return
	}
}

func (uh *UserHandler) Logout(ctx *gin.Context) {
	err := uh.ClearToken(ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Msg: "系统错误",
		})
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}

func (uh *UserHandler) RefreshToken(ctx *gin.Context) {
	tokenStr := uh.ExtractTokenString(ctx)
	var rc jwt3.RefreshClaims
	token, err := jwt.ParseWithClaims(tokenStr, &rc, func(token *jwt.Token) (interface{}, error) {
		return jwt3.RefreshTokenKey, nil
	})

	// 保持和登陆校验一样的逻辑，即返回 401 响应
	if err != nil || token == nil || !token.Valid {
		ctx.JSON(http.StatusUnauthorized, Result{Code: 4, Msg: "请登录"})
		return
	}

	// 校验ssid
	err = uh.CheckSession(ctx, rc.Ssid)
	if err != nil {
		// 系统错误或者用户已经主动退出登录了
		// 这里也可以考虑说，如果在 Redis 已经崩溃的时候，
		// 就不要去校验是不是已经主动退出登录了。
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return
	}

	err = uh.SetJwtToken(ctx, rc.Ssid, rc.Id)
	if err != nil {
		ctx.JSON(http.StatusUnauthorized, Result{Code: 4, Msg: "请登录"})
		return
	}
	ctx.JSON(http.StatusOK, Result{Msg: "登录成功"})
}

func (uh *UserHandler) Edit(ctx *gin.Context) {
	type Req struct {
		NickName string `json:"nickName"`
		// 2024-07-07
		Birthday string `json:"birthday"`
		AboutMe  string `json:"aboutMe"`
	}

	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}

	if req.NickName == "" {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "昵称不能为空"})
		return
	}

	if len(req.AboutMe) > 1024 {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "自我介绍过长"})
		return
	}

	birthday, err := time.Parse(time.DateOnly, req.Birthday)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 4, Msg: "生日格式不对"})
		return
	}

	uc := ctx.MustGet("user").(jwt3.UserClaims)
	_, err = uh.svc.UpdateNonSensitiveInfo(ctx.Request.Context(), &userv1.UpdateNonSensitiveInfoRequest{
		User: &userv1.User{
			Id:       uc.Id,
			Nickname: req.NickName,
			AboutMe:  req.AboutMe,
			Birthday: timestamppb.New(birthday),
		},
	})

	if err != nil {
		ctx.JSON(http.StatusOK, Result{Code: 5, Msg: "系统错误"})
		return
	}
	ctx.JSON(http.StatusOK, Result{Msg: "OK"})
}

// ProfileJwt 用户详情 jwt版本
func (uh *UserHandler) ProfileJwt(ctx *gin.Context) {
	type Profile struct {
		Email    string
		Phone    string
		Nickname string
		Birthday string
		AboutMe  string
	}

	uc := ctx.MustGet("user").(jwt3.UserClaims)
	profile, err := uh.svc.Profile(ctx.Request.Context(), &userv1.ProfileRequest{Id: uc.Id})
	if err != nil {
		ctx.String(http.StatusOK, "系统错误")
		return
	}

	u := profile.User
	ctx.JSON(http.StatusOK, Profile{
		Email:    u.Email,
		Phone:    u.Phone,
		Nickname: u.Nickname,
		Birthday: u.Birthday.AsTime().Format(time.DateOnly),
		AboutMe:  u.AboutMe,
	})
}
